package com.k.kc.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.TimeZone;

/**
 * Date
 *
 * @author LJW
 */
public class DateUtils {
    private final static String PATTERN_SS = "yyyy-MM-dd HH:mm:ss";
    private final static String PATTERN_MS = "yyyy-MM-dd HH:mm:ss.SSS";
    private final static String PATTERN_DATE_T_TIME_SS = "yyyy-MM-dd'T'HH:mm:ss";
    private final static String PATTERN_DATE_T_TIME_SS_Z = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    private final static String PATTERN_DATE_T_TIME_MS = "yyyy-MM-dd'T'HH:mm:ss.SSS";

    public static final String PATTERN_FORMAT_Z = "yyyy-MM-dd'T'HH:mm:ssZ ";

    static final String[] WEEKS = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};

    /**
     * 无时区信息
     * @return Date
     */
    public static Date nowDate(){
        return new Date();
    }

    /**
     * 带时区
     * @param timeZone
     * @return String
     */
    public static String nowDateStrTimeZone(TimeZone timeZone,String format){
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        sdf.setTimeZone(timeZone);
        return sdf.format(nowDate());
    }

    /**
     * 带时区系统默认时区
     * @return String
     */
    public static String nowDateStrDefaultTimeZone(String format){
        return nowDateStrTimeZone(TimeZone.getDefault(),format);
    }

    /**
     * UTC 时间
     * @return String
     */
    public static String nowDateStrUTC(String format){
        return nowDateStrTimeZone(TimeZone.getTimeZone(ZoneId.of("UTC")), format);
    }

    /**
     * 注意
     * 本地时区是 GMT+8 情况
     * date -> instant  自动-8H，因为 Instant 默认时区UTC；
     * Instant ->Date   自动+8H，因为Date默认本地时区；
     *
     * 本地时区是 GMT 情况
     * Date -> Instant 自动 -0H
     * Instant -> Date 自动 +0H
     *
     * @param date date
     * @return
     */
    public static Instant dateToInstant(Date date){
        return date.toInstant();
    }

    /**
     * 格式化
     *
     * @param localDateTime localDateTime
     * @param pattern       pattern
     * @return result
     */
    public static String format(LocalDateTime localDateTime, String pattern) {
        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * Date -> LocalDateTime
     *
     * @param date   date
     * @param zoneId zoneId
     * @return result
     */
    public static LocalDateTime dateToLocalDateTime(Date date, ZoneId zoneId) {
        return date.toInstant().atZone(zoneId).toLocalDateTime();
    }

    /**
     * Date -> LocalDateTime
     *
     * @param date date
     * @return result
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        return dateToLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * LocalDateTime -> Date
     *
     * @param localDateTime localDateTime
     * @param zoneId        zoneId
     * @return result
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime, ZoneId zoneId) {
        return Date.from(localDateTime.atZone(zoneId).toInstant());
    }

    /**
     * LocalDateTime -> Date
     *
     * @param localDateTime localDateTime
     * @return result
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        return localDateTimeToDate(localDateTime, ZoneId.systemDefault());
    }

    public static String toStringE(Instant instant, ZoneId zoneId, String pattern) {
        return LocalDateTime
                .ofInstant(instant, zoneId)
                .format(DateTimeFormatter.ofPattern(pattern));
    }

    public static String toString(Date date, ZoneId zoneId, String pattern) {
        return toStringE(date.toInstant(),zoneId,pattern);
    }

    public static String toString(LocalDateTime ldt, ZoneId zoneId, String pattern) {
        return toStringE(ldt.atZone(zoneId).toInstant(),zoneId,pattern);
    }
    /**
     * 获取当前时间
     *
     * @param zoneId  zoneId
     * @param pattern pattern
     * @return STR
     */
    public static String nowToString(ZoneId zoneId, String pattern) {
        return toStringE(Instant.now(),zoneId,pattern);//.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取当前时间，默认时区
     *
     * @param pattern yyyy-MM-dd HH:mm:ss
     * @return yyyy-MM-dd HH:mm:ss
     */
    public static String nowDefaultToString(String pattern) {
        return nowToString(ZoneId.systemDefault(), pattern);
    }

    /**
     * 获取当前时间 UTC 时间
     *
     * @param pattern pattern
     * @return STR
     */
    public static String nowUtcToString(String pattern) {
        return nowToString(Clock.systemUTC().getZone(), pattern);
    }

    /**
     * UTC 1970至今毫秒数
     *
     * @return long millis
     */
    public static long nowUtcToLong() {
        // Clock.systemUTC().millis() Same as System.currentTimeMillis()
        return System.currentTimeMillis();
    }

    public static Date nowUtcToDate() {
        return new Date(System.currentTimeMillis());
    }

    public static LocalDateTime nowUtcToLocalDateTime() {
        return LocalDateTime.now(Clock.systemUTC());
    }

    public static Date stringToDate(String str, String pattern) throws ParseException {
        return new SimpleDateFormat(pattern).parse(str);
    }
    public static LocalDateTime stringToLocalDateTime(String str, String pattern) {
        return LocalDateTime.parse(str, DateTimeFormatter.ofPattern(pattern));
    }
    /**
     * String -> Date
     *
     * @param str     str
     * @param pattern pattern
     * @return date
     */
    public static LocalDateTime strToDate(String str, String pattern) {
        return LocalDateTime.parse(str, DateTimeFormatter.ofPattern(pattern));
    }

    public static Date strToDate21(String str, String pattern) throws ParseException {
        return new SimpleDateFormat(pattern).parse(str);
    }

    public static Date strToDate22(String str, String pattern) {
        return localDateTimeToDate(strToDate(str, pattern));
    }

    private static ZoneId getZone() {
        /*
         * Asia/Shanghai
         * GMT+8
         * UTC+8
         * UT+8
         * +8
         * ZoneId.systemDefault()
         * Clock.systemUTC().getZone()
         */
        return ZoneId.of("Asia/Shanghai");
    }

    /**
     * 获取当月最大天数
     *
     * @param year  年
     * @param month 月
     * @return 天数
     * @deprecated use getDaysOfMonth() instead
     */
    public static int getMaxDaysOfYearMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        return cal.getActualMaximum(Calendar.DATE);
    }

    /**
     * 日期运算
     * @param srcDate date
     * @param n n
     * @param ymdh y m d h
     * @return Date
     */
    public static Date add(Date srcDate,int n, String ymdh){
        if(n==0){
            return srcDate;
        }
        Calendar cal = Calendar.getInstance();
        //设置起时间
        cal.setTime(srcDate);
        if("y".equalsIgnoreCase(ymdh)){
            cal.add(Calendar.YEAR, n);
        } else if("m".equalsIgnoreCase(ymdh)){
            cal.add(Calendar.MONTH, n);
        } else if("d".equalsIgnoreCase(ymdh)){
            cal.add(Calendar.DATE, n);
        } else if("h".equalsIgnoreCase(ymdh)){
            cal.add(Calendar.HOUR,n);
        } else if("min".equalsIgnoreCase(ymdh)){
            cal.add(Calendar.MINUTE, 1);
        }

        return cal.getTime();
    }

    /**
     * 闰年判断
     *
     * @param year year
     * @return true:loopYear
     */
    public static boolean isLoopYear(int year) {
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }

    /**
     * 获得某月的天数
     *
     * @param year  year
     * @param month month [1,12]
     *              getDaysOfMonth is faster then getMaxDaysOfYearMonth
     */
    public static int getDaysOfMonth(int year, int month) {
        int days;
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                days = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                days = 30;
                break;
            case 2:
                days = isLoopYear(year) ? 29 : 28;
                break;
            default:
                days = 0;
                break;
        }
        return days;
    }

    /**
     * 月份的第一天是星期几
     *
     * @param year  year
     * @param month month
     * @return [0, 1, 2, 3, 4, 5, 6] R ["日","一","二","三","四","五","六"]
     */
    public static int getFirstOfWeekInTheMonth(int year, int month) {
        Calendar calender = Calendar.getInstance();
        calender.set(year, month, 1);
        return calender.get(Calendar.DAY_OF_WEEK) - 1;
    }

    /**
     * 月份的第一天是星期几
     *
     * @param year  year
     * @param month month
     * @return 星期一
     */
    public static String getFirstOfWeekInTheMonthString(int year, int month) {
        return WEEKS[getDaysOfMonth(year, month)];
    }

    /**
     * 返回 min 到 max 的随机整数
     *
     * @param min min
     * @param max max
     * @return randomNum r>=min r<=max
     */
    public static int random(int min, int max) {
        return new Random().nextInt((max - min) + 1) + min;
    }

    public static void printZoneIdOffset() {
        Date date = new Date();
        String delimiter = "";
        for (int i = -11; i <= 11; i++) {
            String offset = "";
            String hour = "";
            if (i == 0) {
                hour = "";
            } else if (i < 0) {
                if (i > -10) {
                    hour = "-0" + (-i) + delimiter + "00";
                } else {
                    hour = "-" + (-i) + delimiter + "00";
                }
            } else if (i > 0) {
                if (i < 10) {
                    hour = "+0" + i + delimiter + "00";
                } else {
                    hour = "+" + i + delimiter + "00";
                }
            } else {
                hour = "+" + i + delimiter + "00";
            }
            offset = "UTC" + hour;
            String zoneId = ZoneId.of(offset).getId();
            String time = date.toInstant().atZone(ZoneId.of(offset)).format(DateTimeFormatter.ofPattern(PATTERN_DATE_T_TIME_SS_Z));
            System.out.println("offset:" + offset + " zoneId:" + zoneId + ": " + time);
        }
    }
    public static String now(){
        return new SimpleDateFormat(PATTERN_SS).format(new Date());
    }
    public static String nowMs(){
        return new SimpleDateFormat(PATTERN_DATE_T_TIME_MS).format(new Date());
    }

    private static void test() {
        // ZonedDateTime. UTC Time
        System.out.println("Instant0:" + Instant.now());
        ZonedDateTime d = Instant.now().atZone(ZoneId.of("UTC+00:00"));
        System.out.println("dateTime:" + d);

        // Instant
        System.out.println("instant1:" + Clock.systemUTC().instant());
        System.out.println("instant2:" + Clock.system(ZoneId.of("UTC+08:00")).instant());
        System.out.println("instant3:" + Clock.system(ZoneId.of("UTC+08:00")).instant().atZone(ZoneId.of("UTC+08:00")));

        // LocalDateTime
        System.out.println("localDateTime0:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern(PATTERN_SS)));
        System.out.println("localDateTime1:" + LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
        System.out.println("localDateTime2:" + LocalDateTime.now(ZoneId.of("Australia/Melbourne")));
        System.out.println("localDateTime3:" + LocalDateTime.now(ZoneId.of("America/Chicago")));

        String str = "2021-06-02T14:50:32.327";
        LocalDateTime dateTime = strToDate(str, PATTERN_DATE_T_TIME_MS);
        System.out.println("str -> date:" + dateTime);

        Long a = 20211119L;
        System.out.println(a);

        String planDateStrMax = a + "000000";
        String planDateStrMin = a + "235959";

        LocalDateTime begin = LocalDateTime.parse(planDateStrMin, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        LocalDateTime end = LocalDateTime.parse(planDateStrMax, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        System.out.println(begin.format(DateTimeFormatter.ofPattern(PATTERN_SS)));
        System.out.println(end.format(DateTimeFormatter.ofPattern(PATTERN_SS)));

        int i = 0;
        for (String zid : ZoneId.getAvailableZoneIds()) {
            i++;
            System.out.println(i + ":" + zid);
        }
        System.out.println("=================================");
        int j = 0;
        for (String zo : ZoneOffset.getAvailableZoneIds()) {
            j++;
            System.out.println(j + ":" + zo);
        }
    }

    /**
     * TEST
     *
     * @param args args
     */
    public static void main(String[] args) throws ParseException {
        String dateStr = "2023-04-18T14:48:43Z";
        String pattern = PATTERN_DATE_T_TIME_SS_Z;

        // str -> Date
        System.out.printf("str:%s -> Date:%s%n",dateStr,DateUtils.stringToDate(dateStr,pattern));
        System.out.printf("str:%s -> LocalDateTime:%s%n",dateStr,DateUtils.stringToLocalDateTime(dateStr,pattern));

        // Date -> str
        System.out.printf("Instant:%s -> str:%s%n",Instant.now(),DateUtils.toStringE(Instant.now(),ZoneId.of("UTC"),pattern));
        System.out.printf("Date:%s -> str:%s%n",DateUtils.nowUtcToDate(),DateUtils.toString(DateUtils.nowUtcToDate(),ZoneId.of("UTC"),pattern));
        System.out.printf("LocalDateTime:%s -> str:%s%n",DateUtils.nowUtcToDate(),DateUtils.toString(DateUtils.nowUtcToLocalDateTime(),ZoneId.of("UTC"),pattern));

        // now -> str
        System.out.println("nowUtc:" + DateUtils.nowToString(ZoneId.of("UTC"),pattern));
        System.out.println("nowUtc:" + DateUtils.nowUtcToString( pattern));
        System.out.println("nowDefault:" + DateUtils.nowDefaultToString(pattern));

        LocalDateTime ldt = LocalDateTime.now();
        LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("UTC"));
        System.out.println(ldt);
        System.out.println(ldt1);
        System.out.println("ldt f:"+DateUtils.toString(ldt,ZoneId.of("UTC"),pattern));
        System.out.println("ldt1 f:"+DateUtils.toString(ldt1,ZoneId.of("UTC"),pattern));
        ldt.toInstant(ZoneOffset.UTC);
        ldt1.toInstant(ZoneOffset.UTC);
        Date date = new Date();
        Date date1 = new Date(System.currentTimeMillis());
        System.out.println(date.getTime());
        System.out.println(date1.getTime());
        System.out.println("date:"+date);
        System.out.println("date1:"+date1);
        String s = date.toInstant().atZone(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(pattern));
        String ss = date.toInstant().atZone(ZoneId.of("UTC+8")).format(DateTimeFormatter.ofPattern(pattern));

        String s1 = date1.toInstant().atZone(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(pattern));
        String ss1 = date1.toInstant().atZone(ZoneId.of("UTC+08:00")).format(DateTimeFormatter.ofPattern(pattern));
        String ss12 = date1.toInstant().atZone(ZoneId.of("UTC+05:30")).format(DateTimeFormatter.ofPattern(pattern));
        System.out.println(s);
        System.out.println(ss);
        System.out.println(s1);
        System.out.println(ss1);
        System.out.println("SS12:"+ss12);

        ZoneId zz = ZoneId.of("America/Toronto");
        System.out.println("zz:"+zz.getId());
        System.out.println("new Date():"+date);
        Instant instant = date.toInstant();
        System.out.println("new Date().toInstant():"+instant);
        LocalDateTime localDateTime = instant.atZone(ZoneId.of("GMT+8")).toLocalDateTime();
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("UTC"));
        System.out.println("zonedDateTime:"+zonedDateTime);
        System.out.println("localDateTime:"+localDateTime);
        // Date无时区 - Instant(无时区) 时刻 - ZoneDateTime(带时区)
        System.out.println(zonedDateTime.getZone());


        Date d1 = new Date();
        Date d2 = DateUtils.add(d1,-24*3,"h");
        LocalDateTime d1d2Str = DateUtils.dateToLocalDateTime(d2);
        System.out.println(d1d2Str);
    }
}
